// $Id: pstring.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <cstring>
#include <boost/test/unit_test.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/string.hpp>
#include <xpcom/proxy/pstring.hpp>

namespace xpcom {
namespace {

const char* s_s1 = "test1";
const char* s_s2 = "test2";
const char* s_s3 = "test3";
const char* s_ss = "";

void do_out_impl_1(char** s)
{
    BOOST_CHECK(*s == 0);
    *s = 0;
}

void do_out_impl_size_1(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s == 0);
    *s = 0;
    *n = 0;
    *l = 0;
}

void do_out_impl_size2_1(char** s, uint32_t* n)
{
    BOOST_CHECK(*s == 0);
    *s = 0;
    *n = 0;
}

void do_out_1(string& s)
{
    do_out_impl_1(proxy::pstr_out(s));
}

void do_out_size_1(string& s)
{
    proxy::pstr_out_size ss(s);
    do_out_impl_size_1(ss, ss.size(), ss.length());
    ss.commit();
}

void do_out_size2_1(string& s)
{
    proxy::pstr_out_size2 ss(s);
    do_out_impl_size2_1(ss, ss.size2());
    ss.commit();
}

void do_out_impl_2(char** s)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_s1, 6);
}

void do_out_impl_size_2(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_s1, 6);
    *n = 5;
    *l = 3;
}

void do_out_impl_size2_2(char** s, uint32_t* n)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_s1, 6);
    *n = 4;
}

void do_out_2(string& s)
{
    do_out_impl_2(proxy::pstr_out(s));
}

void do_out_size_2(string& s)
{
    proxy::pstr_out_size ss(s);
    do_out_impl_size_2(ss, ss.size(), ss.length());
    ss.commit();
}

void do_out_size2_2(string& s)
{
    proxy::pstr_out_size2 ss(s);
    do_out_impl_size2_2(ss, ss.size2());
    ss.commit();
}

void do_out_impl_3(char** s)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_ss, 1);
}

void do_out_impl_size_3(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_ss, 1);
    *n = 1;
    *l = 0;
}

void do_out_impl_size2_3(char** s, uint32_t* n)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_s1, 6);
    *n = 0;
}

void do_out_3(string& s)
{
    do_out_impl_3(proxy::pstr_out(s));
}

void do_out_size_3(string& s)
{
    proxy::pstr_out_size ss(s);
    do_out_impl_size_3(ss, ss.size(), ss.length());
    ss.commit();
}

void do_out_size2_3(string& s)
{
    proxy::pstr_out_size2 ss(s);
    do_out_impl_size2_3(ss, ss.size2());
    ss.commit();
}

void do_inout_impl_1(char** s)
{
    BOOST_CHECK(*s == 0);
    *s = memory::clone(s_s1, 6);
}

void do_inout_impl_size_1(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s == 0);
    BOOST_CHECK(*n == 0);
    BOOST_CHECK(*l == 0);
    *s = memory::clone(s_s1, 6);
    *n = 5;
    *l = 5;
}

void do_inout_impl_size2_1(char** s, uint32_t* n)
{
    BOOST_CHECK(*s == 0);
    BOOST_CHECK(*n == 0);
    *s = memory::clone(s_s1, 6);
    *n = 5;
}

void do_inout_1(string& s)
{
    do_inout_impl_1(proxy::pstr_inout(s));
}

void do_inout_size_1(string& s)
{
    proxy::pstr_inout_size ss(s);
    do_inout_impl_size_1(ss, ss.size(), ss.length());
    ss.commit();
}

void do_inout_size2_1(string& s)
{
    proxy::pstr_inout_size2 ss(s);
    do_inout_impl_size2_1(ss, ss.size2());
    ss.commit();
}

void do_inout_impl_2(char** s)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(std::strcmp(*s, s_s2) == 0);

    std::strcpy(*s, s_s3);
}

void do_inout_impl_size_2(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(*n == 5);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(std::strcmp(*s, s_s2) == 0);

    std::strcpy(*s, s_s3);
    *n = 5;
    *l = 3;
}

void do_inout_impl_size2_2(char** s, uint32_t* n)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(*n == 5);
    BOOST_CHECK(std::strcmp(*s, s_s2) == 0);

    std::strcpy(*s, s_s3);
    *n = 3;
}

void do_inout_2(string& s)
{
    do_inout_impl_2(proxy::pstr_inout(s));
}

void do_inout_size_2(string& s)
{
    proxy::pstr_inout_size ss(s);
    do_inout_impl_size_2(ss, ss.size(), ss.length());
    ss.commit();
}

void do_inout_size2_2(string& s)
{
    proxy::pstr_inout_size2 ss(s);
    do_inout_impl_size2_2(ss, ss.size2());
    ss.commit();
}

void do_inout_impl_3(char** s)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(std::strcmp(*s, s_s1) == 0);

    memory::free(*s);
    *s = memory::clone(s_s2, 6);
}

void do_inout_impl_size_3(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(*n == 5);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(std::strcmp(*s, s_s1) == 0);

    memory::free(*s);
    *s = memory::clone(s_s2, 6);
    *n = 5;
    *l = 4;
}

void do_inout_impl_size2_3(char** s, uint32_t* n)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(*n == 5);
    BOOST_CHECK(std::strcmp(*s, s_s1) == 0);

    memory::free(*s);
    *s = memory::clone(s_s2, 6);
    *n = 4;
}

void do_inout_3(string& s)
{
    do_inout_impl_3(proxy::pstr_inout(s));
}

void do_inout_size_3(string& s)
{
    proxy::pstr_inout_size ss(s);
    do_inout_impl_size_3(ss, ss.size(), ss.length());
    ss.commit();
}

void do_inout_size2_3(string& s)
{
    proxy::pstr_inout_size2 ss(s);
    do_inout_impl_size2_3(ss, ss.size2());
    ss.commit();
}

void do_inout_impl_4(char** s)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(std::strcmp(*s, s_s2) == 0);

    memory::free(*s);
    *s = 0;
}

void do_inout_impl_size_4(char** s, uint32_t* n, uint32_t* l)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(*n == 5);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(std::strcmp(*s, s_s2) == 0);

    memory::free(*s);
    *s = 0;
    *n = 0;
    *l = 0;
}

void do_inout_impl_size2_4(char** s, uint32_t* n)
{
    BOOST_CHECK(*s != 0);
    BOOST_CHECK(*n == 5);
    BOOST_CHECK(std::strcmp(*s, s_s2) == 0);

    memory::free(*s);
    *s = 0;
    *n = 0;
}

void do_inout_4(string& s)
{
    do_inout_impl_4(proxy::pstr_inout(s));
}

void do_inout_size_4(string& s)
{
    proxy::pstr_inout_size ss(s);
    do_inout_impl_size_4(ss, ss.size(), ss.length());
    ss.commit();
}

void do_inout_size2_4(string& s)
{
    proxy::pstr_inout_size2 ss(s);
    do_inout_impl_size2_4(ss, ss.size2());
    ss.commit();
}

void do_out_isize2_impl_1(char** s, uint32_t n)
{
    BOOST_CHECK(*s == 0);
    *s = memory::alloc<char>(n + 1);
    std::memset(*s, 'x', n * sizeof(char));
    (*s)[n] = '\0';
}

void do_out_isize2_1(string& s, uint32_t n)
{
    do_out_isize2_impl_1(proxy::pstr_out_isize2(s, n), n);
}

void do_out_isize2_impl_2(char** s, uint32_t n)
{
    BOOST_CHECK(*s == 0);
    *s = 0;
}

void do_out_isize2_2(string& s, uint32_t n)
{
    do_out_isize2_impl_2(proxy::pstr_out_isize2(s, n), n);
}

}
}

BOOST_AUTO_TEST_CASE(test_pstr_in)
{
    using namespace xpcom;

    const char* s1 = s_s1;
    const char* s2 = 0;
    string str1 = s_s1;
    string str2;

    BOOST_CHECK(proxy::pstr_in(s1) == s_s1);
    BOOST_CHECK(proxy::pstr_in(s2) == 0);
    BOOST_CHECK(std::strcmp(proxy::pstr_in(str1), s_s1) == 0);
    BOOST_CHECK(proxy::pstr_in(str2) == 0);
}

BOOST_AUTO_TEST_CASE(test_pstr_out)
{
    using namespace xpcom;

    string s;
    do_out_1(s);
    BOOST_CHECK(s.empty());

    do_out_2(s);
    BOOST_CHECK(s == s_s1);

    do_out_3(s);
    BOOST_CHECK(s.empty());
}

BOOST_AUTO_TEST_CASE(test_pstr_out_size)
{
    using namespace xpcom;

    string s;
    do_out_size_1(s);
    BOOST_CHECK(s.empty());

    do_out_size_2(s);
    BOOST_CHECK(s == "tes");

    do_out_size_3(s);
    BOOST_CHECK(s.empty());
}

BOOST_AUTO_TEST_CASE(test_pstr_out_size2)
{
    using namespace xpcom;

    string s;
    do_out_size2_1(s);
    BOOST_CHECK(s.empty());

    do_out_size2_2(s);
    BOOST_CHECK(s == "test");

    do_out_size2_3(s);
    BOOST_CHECK(s.empty());
}

BOOST_AUTO_TEST_CASE(test_pstr_inout)
{
    using namespace xpcom;

    string s;

    do_inout_1(s);
    BOOST_CHECK(s == s_s1);

    s = s_s2;
    do_inout_2(s);
    BOOST_CHECK(s == s_s3);

    s = s_s1;
    do_inout_3(s);
    BOOST_CHECK(s == s_s2);

    s = s_s2;
    do_inout_4(s);
    BOOST_CHECK(s.empty());
}

BOOST_AUTO_TEST_CASE(test_pstr_inout_size)
{
    using namespace xpcom;

    string s;

    do_inout_size_1(s);
    BOOST_CHECK(s == s_s1);

    s = s_s2;
    do_inout_size_2(s);
    BOOST_CHECK(s == "tes");

    s = s_s1;
    do_inout_size_3(s);
    BOOST_CHECK(s == "test");

    s = s_s2;
    do_inout_size_4(s);
    BOOST_CHECK(s.empty());
}

BOOST_AUTO_TEST_CASE(test_pstr_inout_size2)
{
    using namespace xpcom;

    string s;

    do_inout_size2_1(s);
    BOOST_CHECK(s == s_s1);

    s = s_s2;
    do_inout_size2_2(s);
    BOOST_CHECK(s == "tes");

    s = s_s1;
    do_inout_size2_3(s);
    BOOST_CHECK(s == "test");

    s = s_s2;
    do_inout_size2_4(s);
    BOOST_CHECK(s.empty());
}

BOOST_AUTO_TEST_CASE(test_pstr_out_isize2)
{
    using namespace xpcom;

    string s;

    do_out_isize2_1(s, 5);
    BOOST_CHECK(s == "xxxxx");

    do_out_isize2_1(s, 0);
    BOOST_CHECK(s.empty());

    do_out_isize2_2(s, 5);
    BOOST_CHECK(s.empty());

    do_out_isize2_2(s, 0);
    BOOST_CHECK(s.empty());
}
